home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
listings
/
v_11_08
/
weber
/
dialog.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-11
|
67KB
|
1,741 lines
/***************************************************************
* file: DIALOG.C
* purpose:
* dialog box functions for simple gui
* for now all dialog boxes are modal
* contains:
* dialog_open(DIALOG_ITEM dialog[],short number_of_items,short loc_x,short loc_y,short width,short height); draws a dialog box
* dialog_message_handler(MESSAGE *message,DIALOG_ITEM *dialog); handles dialog box messages
* dialog_close(DIALOG_ITEM dialog[]); erases a dialog box
* dialog_add_item(short type,void *data,DIALOG_ITEM dialog[]); add an item to an open dialog
* editbox_initialize(EDITBOX *edit); initialize an editbox with a new string
* listbox_initialize(LISTBOX *list); initialize a listbox with a new list
* radiobutton_set(RADIOBUTTON *radio,short id); sets a particular radiobutton
* checkbox_set(BUTTON *button,short id); sets a checkbox
* checkbox_clear(BUTTON *button,short id); clears a checkbox
* system: Written for the flash graphics library in Zortech 3.0
* copyright: 1992 by David Weber. All rights reserved.
* This software can be used for any purpose as object, library or executable.
* It cannot be sold for profit as source code.
* history:
* 01-01-92 - initial code
* 01-31-93 - this code is now obsolete, see the CPP gui package
**************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "gui.h"
/* local defines */
#define FOCUS_CLEAR 0
#define FOCUS_SET 1
/* local prototypes */
static void dialog_focus_draw(DIALOG_ITEM *dialog,short type);
static short dialog_refocus(DIALOG_ITEM dialog[],DIALOG_ITEM *item);
static void dialog_focus_next(DIALOG_ITEM dialog[]);
static void dialog_focus_previous(DIALOG_ITEM dialog[]);
static short text_open(short x,short y,TEXT *text,fg_pbox_t area,DIALOG_ITEM *d);
static short dialog_draw_text(short x,short y,char *str,fg_pbox_t clip);
static short dialog_draw_gray_text(short x,short y,char *str,fg_pbox_t clip);
static short button_open(short x,short y,BUTTON *button,fg_pbox_t area,DIALOG_ITEM *d);
static short checkbox_open(short x,short y,BUTTON *button,fg_pbox_t area,DIALOG_ITEM *d);
static void checkbox_draw(short x,short y,BUTTON *b,fg_pbox_t clip,fg_pbox_t focus);
static void checkbox_draw_gray(short x,short y,BUTTON *b,fg_pbox_t clip,fg_pbox_t focus);
static void checkbox_mark(BUTTON *b);
static void checkbox_unmark(BUTTON *b);
static short radiobutton_open(short x,short y,RADIOBUTTON *radio,fg_pbox_t area,DIALOG_ITEM *d);
static void radiobutton_mark(BUTTON *b,DIALOG_ITEM *d);
static void radiobutton_unmark(BUTTON *b);
static BUTTON *radiobutton_unmark_all(BUTTON *first,short number_of_buttons);
static BUTTON *radiobutton_previous(BUTTON *first,short number_of_buttons);
static BUTTON *radiobutton_next(BUTTON *first,short number_of_buttons);
static short listbox_open(short x,short y,LISTBOX *list,fg_pbox_t area,DIALOG_ITEM *d);
static void listbox_close(LISTBOX *list);
static void listbox_free(LISTBOX *list);
static void listbox_draw_contents(LISTBOX *list);
static short listbox_allocate_page(LISTBOX *list,short type);
static void listbox_mark(LISTBOX *list);
static short listbox_scroll(LISTBOX *list,short number_of_lines);
static char *listbox_current_data(LISTBOX *list,short offset);
static short editbox_open(short x,short y,EDITBOX *edit,fg_pbox_t area,DIALOG_ITEM *d);
static void editbox_close(EDITBOX *edit);
static void editbox_draw(EDITBOX *edit);
static void editbox_cursor(EDITBOX *edit);
static short editbox_edit(EDITBOX *edit,short key);
/************************************************
* function: short dialog_open(DIALOG_ITEM dialog[],short number_of_items,short loc_x,short loc_y,short width,short height)
* registers a dialog box, initializes all the hotspots and draws it
* In this simple gui dialog boxes are always modal
* parameters: array of dialog items in dialog box, number of items in array,
* location of box relative to lower left corner of screen in quarter text
* cell units, width and height of box also in quarter text cell units.
* If loc_x or loc_y is -1 then the box is centered in the screen
* returns: 1 if opened or 0 if failed because of lack of resources
************************************************/
short dialog_open(DIALOG_ITEM dialog[],short number_of_items,short loc_x,short loc_y,short width,short height)
{
short i,j,fail;
DIALOG_ITEM *d,*d2;
fg_box_t dialog_area;
MESSAGE error;
i = 0; /* verify parameters */
if (number_of_items < 0 || number_of_items > DIALOG_MAX_ITEMS)
i = 1;
else
for (j = 0 ; j < number_of_items ; j++)
{
d = &dialog[j];
if (d->type < DIALOG_MIN_TYPE || d->type > DIALOG_MAX_TYPE || d->data == NULL)
i = 1;
}
if (i)
{
error.id = gui_errno = M_INVALID_PARMS;
error.data.ptr_data = dialog_open;
message_send(&error);
return 0;
}
fg_msm_hidecursor();
loc_x = (gui_char_width * loc_x) / DIALOG_UNITS; /* screen units */
loc_y = (gui_char_height * loc_y) / DIALOG_UNITS;
width = (gui_char_width * width) / DIALOG_UNITS;
height = (gui_char_height * height) / DIALOG_UNITS;
if (loc_x < 0 || loc_y < 0) /* center it? */
{
loc_x = (fg.displaybox[FG_X2] - fg.displaybox[FG_X1])/2 - width/2;
if (loc_x < 0) loc_x = 0;
loc_y = (fg.displaybox[FG_Y2] - fg.displaybox[FG_Y1])/2 - height/2;
if (loc_y < 0) loc_y = 0;
}
loc_x += fg.displaybox[FG_X1];
loc_y += fg.displaybox[FG_Y1];
if (loc_x + width > fg.displaybox[FG_X2]) /* adjust box to fit screen */
{
loc_x = fg.displaybox[FG_X2] - width;
if (loc_x < 0)
loc_x = 0;
}
if (loc_y + height > fg.displaybox[FG_Y2])
{
loc_y = fg.displaybox[FG_Y2] - height;
if (loc_y < 0)
loc_y = 0;
}
dialog_area[FG_X1] = loc_x; /* coordinates of dialog box */
dialog_area[FG_X2] = loc_x + width;
dialog_area[FG_Y1] = loc_y;
dialog_area[FG_Y2] = loc_y + height;
fg_boxclip(fg.displaybox,dialog_area,dialog_area);
if (!object_add(OBJECT_DIALOG,(GENERIC_MESSAGE_HANDLER)dialog_message_handler,dialog,dialog_area))
{ /* add object to active list */
fg_msm_showcursor();
fg_flush();
return 0;
}
fg_fillbox(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,dialog_area); /* draw box */
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,dialog_area,fg.displaybox);
for (i=0, d2=NULL, fail=0 ; i < number_of_items ; i++)
{
d = &dialog[i];
d->dialog_state = DIALOG_FIXED;
switch (d->type) /* set up hotspots and draw items */
{
case DIALOG_TEXT:
fail = text_open(loc_x,loc_y,(TEXT *)d->data,dialog_area,d);
break;
case DIALOG_BUTTON:
fail = button_open(loc_x,loc_y,(BUTTON *)d->data,dialog_area,d);
break;
case DIALOG_CHECKBOX:
fail = checkbox_open(loc_x,loc_y,(BUTTON *)d->data,dialog_area,d);
break;
case DIALOG_RADIOBUTTON:
fail = radiobutton_open(loc_x,loc_y,(RADIOBUTTON *)d->data,dialog_area,d);
break;
case DIALOG_LISTBOX:
fail = listbox_open(loc_x,loc_y,(LISTBOX *)d->data,dialog_area,d);
break;
case DIALOG_EDITBOX:
fail = editbox_open(loc_x,loc_y,(EDITBOX *)d->data,dialog_area,d);
break;
default:
break;
}
d->next = NULL; /* thread linked list */
if (d2 != NULL)
d2->next = d;
d2 = d;
if (fail) /* handle errors */
{
if (fail != M_NULL_ERROR) /* null error means error message was already sent */
{
error.id = gui_errno = fail;
error.data.ptr_data = dialog_open;
message_send(&error);
}
dialog_close(dialog);
fg_msm_showcursor();
fg_flush();
return 0;
}
}
exclusive_focus_set(dialog); /* force exclusive focus on dialog box */
d->dialog_state |= DIALOG_FOCUS_HERE; /* local focus on first dialog item */
dialog_focus_next(dialog);
fg_msm_showcursor();
fg_flush();
return 1;
}
/************************************************
* function: void dialog_message_handler(MESSAGE *message,DIALOG_ITEM *dialog)
* handles messages for dialog boxes, the following transforms occur:
* BUTTON - when selected the button id is returned as a message id
* CHECKBOX - when selected the checkbox id is returned as a message id,
* message->data.short_data.x is 1 if box is on or 0 if off
* RADIOBUTTON - when any button in the radio button set is selected the
* radio set id is returned as the message id, the id of the
* button in the set which was selected is returned as
* message->data.short_data.x and the button which was deselected
* is returned as message->data.short_data.y
* LISTBOX - when any item displayed in a list box is selected with either
* the mouse or with the RETURN key, the listbox id is
* returned as the message id and a pointer to the string is
* returned as message->data.ptr_data
* EDITBOX - If the RETURN key is hit while the editbox has the focus, the
* editbox id is returned as the message id and
* message->data.short_data.x is EDITBOX_ACCEPT. If a change
* is made to the contents of an editbox, the editbox id is
* returned as the message id and message->data.short_data.x
* is EDITBOX_CHANGE
* parameters: pointer to message, pointer to object data
* returns: nothing
************************************************/
void dialog_message_handler(MESSAGE *message,DIALOG_ITEM *dialog)
{
DIALOG_ITEM *d;
BUTTON *b,*b2;
RADIOBUTTON *r;
LISTBOX *l;
EDITBOX *e;
char *p;
short i,key,x,y,focus;
switch (message->id)
{
case M_KEY: /*** KEY MESSAGE ***/
key = message->data.short_data.x;
if (key == TAB) /* focus on next */
{
dialog_focus_next(dialog);
message->id = M_NONE;
return;
}
if (key == SHIFTTAB) /* focus on previous */
{
dialog_focus_previous(dialog);
message->id = M_NONE;
return;
}
for (d = dialog ; d != NULL ; d = d->next)
{
if (d->dialog_state & DIALOG_FOCUS_HERE) /* does this one have the focus? */
focus = 1;
else
focus = 0;
switch (d->type)
{
case DIALOG_CHECKBOX:
b = (BUTTON *) d->data;
if (is_active(*b))
if (b->accelerator == key || ((key == RETURN || key == SPACE) && focus))
{
if (is_selected(*b))
{
checkbox_unmark(b);
message->data.short_data.x = 0;
}
else
{
checkbox_mark(b);
message->data.short_data.x = 1;
}
dialog_refocus(dialog,d);
message->id = b->id;
return;
}
break;
case DIALOG_BUTTON:
b = (BUTTON *) d->data;
if (is_active(*b))
if (b->accelerator == key || (key == RETURN && focus))
{
dialog_refocus(dialog,d);
message->id = b->id;
return;
}
break;
case DIALOG_RADIOBUTTON:
r = (RADIOBUTTON *) d->data;
if (is_active(*r))
{
for (i = 0, b = r->buttons ; i < r->number_of_buttons ; i++, b++)
if (is_active(*b))
if (b->accelerator == key || ((key == RETURN || key == SPACE || key == DOWNARROW || key == UPARROW || key == RIGHTARROW || key == LEFTARROW) && focus))
{
if (b->accelerator != key)
{
if (key == UPARROW || key == LEFTARROW)
b = radiobutton_previous(r->buttons,r->number_of_buttons);
else
b = radiobutton_next(r->buttons,r->number_of_buttons);
}
b2 = radiobutton_unmark_all(r->buttons,r->number_of_buttons);
dialog_refocus(dialog,d);
if (b2 == NULL)
message->data.short_data.y = 0;
else
message->data.short_data.y = b2->id;
message->data.short_data.x = b->id;
radiobutton_mark(b,d);
message->id = r->id;
return;
}
}
break;
case DIALOG_LISTBOX:
l = (LISTBOX *) d->data;
if (is_active(*l) && focus)
{
i = 0;
if (key == UPARROW) i = -1;
if (key == DOWNARROW) i = 1;
if (key == PGUP) i = -l->height/DIALOG_UNITS;
if (key == PGDN) i = l->height/DIALOG_UNITS;
if (key == CTRLPGUP || key == CTRLHOME) i = -10000;
if (key == CTRLPGDN || key == CTRLEND) i = 10000;
if (i)
{
listbox_scroll(l,i);
message->id = M_NONE;
return;
}
if (key == RETURN)
{
if ((p = listbox_current_data(l,l->screen_offset)) == NULL)
break;
message->id = l->id;
message->data.ptr_data = p;
return;
}
}
break;
case DIALOG_EDITBOX:
e = (EDITBOX *) d->data;
if (is_active(*e) && focus)
{
if (key == RETURN)
{
message->id = e->id;
message->data.short_data.x = 1;
return;
}
i = editbox_edit(e,key);
if (i == 2)
{
message->id = e->id;
message->data.short_data.x = 0;
return;
}
if (i == 1)
{
message->id = M_NONE;
return;
}
}
break;
default:
break;
}
}
break;
case M_MOUSE_LEFT: /*** MOUSE MESSAGE ***/
case M_MOUSE_CENTER:
case M_MOUSE_RIGHT:
x = message->data.short_data.x;
y = message->data.short_data.y;
for (d = dialog ; d != NULL ; d = d->next)
{
switch (d->type)
{
case DIALOG_BUTTON:
case DIALOG_CHECKBOX:
b = (BUTTON *) d->data;
if (is_active(*b))
{
if (fg_pt_inbox(b->screen,x,y))
{ /* dialog item selected */
dialog_refocus(dialog,d);
if (d->type == DIALOG_CHECKBOX) /* mark or unmark checkbox */
{
if (is_selected(*b))
{
checkbox_unmark(b);
message->data.short_data.x = 0;
}
else
{
checkbox_mark(b);
message->data.short_data.x = 1;
}
}
message->id = b->id;
return;
}
}
break;
case DIALOG_RADIOBUTTON:
r = (RADIOBUTTON *) d->data;
if (is_active(*r))
{
for (i = 0, b = r->buttons ; i < r->number_of_buttons ; i++, b++)
if (fg_pt_inbox(b->screen,x,y) && is_active(*b))
{
dialog_refocus(dialog,d);
b2 = radiobutton_unmark_all(r->buttons,r->number_of_buttons);
if (b2 == NULL)
message->data.short_data.y = 0;
else
message->data.short_data.y = b2->id;
message->data.short_data.x = b->id;
radiobutton_mark(b,d);
message->id = r->id;
return;
}
}
break;
case DIALOG_LISTBOX:
l = (LISTBOX *) d->data;
if (is_active(*l))
{
if (fg_pt_inbox(l->screen,x,y))
dialog_refocus(dialog,d);
i = 0;
if (fg_pt_inbox(l->up,x,y))
i = -l->height/DIALOG_UNITS;
if (fg_pt_inbox(l->down,x,y))
i = l->height/DIALOG_UNITS;
if (i)
{
listbox_scroll(l,i);
message->id = M_NONE;
return;
}
if (fg_pt_inbox(l->listbox,x,y))
{
i = (l->listbox[FG_Y2]-y)/gui_char_height;
if ((p = listbox_current_data(l,i)) == NULL)
break;
l->screen_offset = i;
listbox_mark(l);
message->id = l->id;
message->data.ptr_data = p;
return;
}
if (fg_pt_inbox(l->marker,x,y))
{
i = (l->listbox[FG_Y2]-y)/gui_char_height;
if ((p = listbox_current_data(l,i)) == NULL)
break;
l->screen_offset = i;
listbox_mark(l);
message->id = M_NONE;
return;
}
}
break;
case DIALOG_EDITBOX:
e = (EDITBOX *) d->data;
if (is_active(*e))
{
if (fg_pt_inbox(e->screen,x,y))
{
dialog_refocus(dialog,d);
i = (x - e->screen[FG_X1] - 1)/gui_char_width;
if (i >= e->screen_width/DIALOG_UNITS)
i = e->screen_width/DIALOG_UNITS - 1;
e->xpos = e->scroll_offset + i;
p = e->edit_string;
i = e->xpos;
if (i >= strlen(p))
{
memset(p+strlen(p),' ',i-strlen(p)+1);
p[i+1] = 0;
}
editbox_cursor(e);
message->id = M_NONE;
return;
}
}
break;
default:
break;
}
}
break;
default:
return;
}
}
/************************************************
* function: short dialog_close(DIALOG_ITEM dialog[])
* parameters: pointer to previously opened dialog box
* returns: 1 if OK or 0 if failed
************************************************/
short dialog_close(DIALOG_ITEM dialog[])
{
short ret;
DIALOG_ITEM *d,*d2;
fg_msm_hidecursor();
exclusive_focus_clear(dialog); /* remove focus restriction */
if ((ret = object_remove(dialog)) != 0)
for (d = dialog ; d != NULL ; d = d2)
{
if (d->type == DIALOG_CHECKBOX)
((BUTTON *) d->data)->status &= ~DIALOG_OPENED;
if (d->type == DIALOG_RADIOBUTTON)
((RADIOBUTTON *) d->data)->status &= ~DIALOG_OPENED;
if (d->type == DIALOG_LISTBOX)
listbox_close((LISTBOX *) d->data);
if (d->type == DIALOG_EDITBOX)
editbox_close((EDITBOX *) d->data);
d2 = d->next;
if (d->dialog_state == DIALOG_ALLOCATED)
free(d);
}
fg_msm_showcursor();
fg_flush();
return ret;
}
/************************************************
* function: short dialog_add_item(short type,void *data,DIALOG_ITEM dialog[])
* parameters: type of item, item's data, opened dialog box
* returns: 1 if added or 0 if failed
************************************************/
short dialog_add_item(short type,void *data,DIALOG_ITEM dialog[])
{
short fail,x,y;
GOB *o;
DIALOG_ITEM *d,*d2;
MESSAGE error;
if (type < DIALOG_MIN_TYPE || type > DIALOG_MAX_TYPE || data == NULL)
{ /* is it proper? */
error.id = gui_errno = M_INVALID_PARMS;
error.data.ptr_data = dialog_add_item;
message_send(&error);
return 0;
}
if ((o = object_exists(dialog)) == NULL)
{ /* is it there? */
error.id = gui_errno = M_NOT_OPEN;
error.data.ptr_data = dialog_add_item;
message_send(&error);
return 0;
}
if ((d = (DIALOG_ITEM *) malloc(sizeof (DIALOG_ITEM))) == NULL)
{ /* are there resources? */
error.id = gui_errno = M_NOMEM;
error.data.ptr_data = dialog_add_item;
message_send(&error);
return 0;
}
d->dialog_state = DIALOG_ALLOCATED; /* build DIALOG_ITEM */
d->type = type;
d->data = data;
d->next = NULL;
fail = 0;
x = o->screen[FG_X1];
y = o->screen[FG_Y1];
fg_msm_hidecursor();
switch (type) /* set up hotspots and draw items */
{
case DIALOG_TEXT:
fail = text_open(x,y,(TEXT *)data,o->screen,d);
break;
case DIALOG_BUTTON:
fail = button_open(x,y,(BUTTON *)data,o->screen,d);
break;
case DIALOG_CHECKBOX:
fail = checkbox_open(x,y,(BUTTON *)data,o->screen,d);
break;
case DIALOG_RADIOBUTTON:
fail = radiobutton_open(x,y,(RADIOBUTTON *)data,o->screen,d);
break;
case DIALOG_LISTBOX:
fail = listbox_open(x,y,(LISTBOX *)data,o->screen,d);
break;
case DIALOG_EDITBOX:
fail = editbox_open(x,y,(EDITBOX *)data,o->screen,d);
break;
default:
break;
}
fg_msm_showcursor();
fg_flush();
if (fail)
{ /* is it platable? */
free(d);
error.id = gui_errno = fail;
error.data.ptr_data = dialog_add_item;
message_send(&error);
return 0;
}
for (d2 = dialog ; d2->next != NULL ; d2 = d2->next)
;
/* thread it on the end */
d2->next = d;
return 1;
}
/************************************************
* function: short editbox_initialize(EDITBOX *edit)
* redraw the editbox with a new string, call this after you change the
* string in the editbox structure
* parameters: pointer to opened editbox
* returns: 1 if OK or 0 if failed
************************************************/
short editbox_initialize(EDITBOX *edit)
{
MESSAGE error;
if ((edit->status & DIALOG_OPENED) == 0)
{ /* cannot initialize an unopened box */
error.id = gui_errno = M_NOT_OPEN;
error.data.ptr_data = editbox_initialize;
message_send(&error);
return 0;
}
edit->scroll_offset = edit->xpos = edit->insert = edit->curtype = edit->curpos = 0;
editbox_draw(edit);
return 1;
}
/************************************************
* function: short listbox_initialize(LISTBOX *list)
* clear listbox and redraw contents, call this if you change the
* first_item next_item functions in the list data structure
* parameters: pointer to listbox
* returns: 1 if OK or 0 if failed
************************************************/
short listbox_initialize(LISTBOX *list)
{
MESSAGE error;
if ((list->status & DIALOG_OPENED) == 0)
{ /* cannot initialize an unopened box */
error.id = gui_errno = M_NOT_OPEN;
error.data.ptr_data = listbox_initialize;
message_send(&error);
return 0;
}
list->status &= ~DIALOG_COMPLETED;
if (list->first_page != NULL) /* clean up old pages */
listbox_free(list);
if (!listbox_allocate_page(list,LISTBOX_FIRST_PAGE)) /* get first page */
return 0;
listbox_draw_contents(list); /* display it */
listbox_mark(list); /* display marker */
return 1;
}
/************************************************
* function: short radiobutton_set(RADIOBUTTON *radio,short id)
* set a button in a radiobutton set using the unique id associated with a
* button to identify it.
* parameters: radio button, unique id of button in radiobutton to set
* returns: 1 if set or 0 if not
************************************************/
short radiobutton_set(RADIOBUTTON *radio,short id)
{
BUTTON *old,*new,*b;
short i;
for (i = 0, b = radio->buttons, old = new = NULL ; i < radio->number_of_buttons ; i++, b++)
{
if (b->id == id)
new = b;
if (is_selected(*b))
old = b;
}
if (new == NULL || is_inactive(*new))
return 0;
if (radio->status & DIALOG_OPENED)
{
radiobutton_unmark_all(radio->buttons,radio->number_of_buttons);
radiobutton_mark(new,radio->dialog);
}
else
{
if (old != NULL)
old->status &= ~DIALOG_SELECTED;
new->status |= DIALOG_SELECTED;
}
return 1;
}
/************************************************
* function: short checkbox_set(BUTTON *button,short id)
* set a checkbox to on
* parameters: checkbox button to set and the unique id of the button
* returns: 1 if set or 0 if not
************************************************/
short checkbox_set(BUTTON *button,short id)
{
if (button->id != id)
return 0;
if (button->status & DIALOG_OPENED)
checkbox_mark(button);
else
button->status |= DIALOG_SELECTED;
return 1;
}
/************************************************
* function: short checkbox_clear(BUTTON *button,short id)
* set a checkbox to off
* parameters: checkbox button to clear and the unique id of the button
* returns: 1 if set or 0 if not
************************************************/
short checkbox_clear(BUTTON *button,short id)
{
if (button->id != id)
return 0;
if (button->status & DIALOG_OPENED)
checkbox_unmark(button);
else
button->status &= ~DIALOG_SELECTED;
return 1;
}
/* ---------------- LOCAL FUNCTIONS ---------------- */
/* draw a dialog focus, type is FOCUS_CLEAR or FOCUS_SET */
static void dialog_focus_draw(DIALOG_ITEM *dialog,short type)
{
short color1,color2;
fg_box_t box;
if (type == FOCUS_SET)
color1 = color2 = COLOR_DIALOG_FOCUS;
else
{
color1 = COLOR_DIALOG_FOREGROUND;
color2 = COLOR_DIALOG_BACKGROUND;
}
fg_msm_hidecursor();
fg_box_cpy(box,dialog->focus);
fg_drawbox(color1,FG_MODE_SET,~0,FG_LINE_SOLID,box,fg.displaybox);
box[FG_X1]--;
box[FG_Y1]--;
box[FG_X2]++;
box[FG_Y2]++;
fg_drawbox(color2,FG_MODE_SET,~0,FG_LINE_SOLID,box,fg.displaybox);
fg_msm_showcursor();
fg_flush();
}
/* clear all dialog foci and refocus on item */
static short dialog_refocus(DIALOG_ITEM dialog[],DIALOG_ITEM *item)
{
DIALOG_ITEM *d;
if (item->dialog_state & DIALOG_FOCUS_SKIP) /* skip it? */
return 0;
for (d = dialog ; d != NULL ; d = d->next) /* clear all foci */
if (d->dialog_state & DIALOG_FOCUS_HERE)
{
dialog_focus_draw(d,FOCUS_CLEAR);
d->dialog_state &= ~DIALOG_FOCUS_HERE;
}
dialog_focus_draw(item,FOCUS_SET); /* set new focus */
item->dialog_state |= DIALOG_FOCUS_HERE;
return 1;
}
/* focus on the next dialog object */
static void dialog_focus_next(DIALOG_ITEM dialog[])
{
DIALOG_ITEM *current,*mark;
for (current = dialog ; current != NULL ; current = current->next)
if (current->dialog_state & DIALOG_FOCUS_HERE) /* find focus */
break;
if (current == NULL)
current = dialog;
mark = current;
do /* find next */
{
if (current->next == NULL)
current = dialog;
else
current = current->next;
if (current == mark)
break;
} while (current->dialog_state & DIALOG_FOCUS_SKIP);
dialog_refocus(dialog,current);
}
/* focus on the previous dialog object */
static void dialog_focus_previous(DIALOG_ITEM dialog[])
{
DIALOG_ITEM *d,*current,*mark;
for (current = dialog ; current != NULL ; current = current->next)
if (current->dialog_state & DIALOG_FOCUS_HERE) /* find focus */
break;
if (current == NULL)
current = dialog;
mark = current;
do /* find previous */
{
for (d = dialog ; d->next != current && d->next != NULL ; d = d->next)
;
current = d;
if (current == mark)
break;
} while (current->dialog_state & DIALOG_FOCUS_SKIP);
dialog_refocus(dialog,current);
}
/* ---------------- TEXT ---------------- */
/* verify parameters and draw text, returns 0 if OK else returns error message */
static short text_open(short x,short y,TEXT *text,fg_pbox_t area,DIALOG_ITEM *d)
{
if (text->loc_x < 0 || text->loc_y < 0 || text->name == NULL || (text->status & ~DIALOG_BITS))
return M_INVALID_PARMS;
d->focus[FG_X1] = x+(gui_char_width*text->loc_x)/DIALOG_UNITS;
d->focus[FG_Y1] = y+(gui_char_height*text->loc_y)/DIALOG_UNITS;
if (is_gray(*text))
d->focus[FG_X2] = dialog_draw_gray_text(d->focus[FG_X1],d->focus[FG_Y1],text->name,area);
if (is_active(*text))
d->focus[FG_X2] = dialog_draw_text(d->focus[FG_X1],d->focus[FG_Y1],text->name,area);
d->focus[FG_Y2] = d->focus[FG_Y1] + gui_char_height;
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* draw dialog text str at coordinates x,y using '&' highlighting, return new x value */
static short dialog_draw_text(short x,short y,char *str,fg_pbox_t clip)
{
short i,color;
for (i = 0 ; str[i] != 0 ; i++)
{
color = COLOR_DIALOG_FOREGROUND;
if (str[i] == '&')
{
i++;
if (str[i] == 0)
i--;
if (str[i] != '&')
color = COLOR_DIALOG_HIGHLIGHT;
}
fg_putc(color,FG_MODE_SET,~0,FG_ROT0,x,y,str[i],clip);
x += gui_char_width;
}
return x;
}
/* draw gray dialog text str at coordinates x,y; return new x value */
static short dialog_draw_gray_text(short x,short y,char *str,fg_pbox_t clip)
{
short i;
for (i = 0 ; str[i] != 0 ; i++)
{
if (str[i] == '&')
{
i++;
if (str[i] == 0)
i--;
}
fg_putc(COLOR_DIALOG_GRAY,FG_MODE_SET,~0,FG_ROT0,x,y,str[i],clip);
x += gui_char_width;
}
return x;
}
/* ---------------- BUTTON ---------------- */
/* verify parameters and draw button, returns 0 if OK else returns error message */
static short button_open(short x,short y,BUTTON *button,fg_pbox_t area,DIALOG_ITEM *d)
{
short cell_height,color;
fg_box_t temp;
if (button->name == NULL || (button->status & ~DIALOG_BITS) ||
button->accelerator < KEY_MIN || button->accelerator > KEY_MAX)
return M_INVALID_PARMS;
if (is_active(*button) || is_gray(*button))
{
cell_height = gui_char_height + 6;
if (gui_char_height + gui_char_height/2 > cell_height)
cell_height = gui_char_height + gui_char_height/2;
temp[FG_X1] = x+(gui_char_width*button->loc_x)/DIALOG_UNITS;
temp[FG_Y1] = y+(gui_char_height*button->loc_y)/DIALOG_UNITS;
if (is_gray(*button))
{
temp[FG_X2] = dialog_draw_gray_text(temp[FG_X1],temp[FG_Y1],button->name,area);
color = COLOR_DIALOG_GRAY;
d->dialog_state |= DIALOG_FOCUS_SKIP;
}
else
{
temp[FG_X2] = dialog_draw_text(temp[FG_X1],temp[FG_Y1],button->name,area);
color = COLOR_DIALOG_FOREGROUND;
}
temp[FG_X1] -= gui_char_width;
temp[FG_X2] += gui_char_width;
temp[FG_Y1] -= (cell_height-gui_char_height)/2;
temp[FG_Y2] = temp[FG_Y1] + cell_height;
fg_drawbox(color,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
fg_box_cpy(button->screen,temp);
fg_box_cpy(d->focus,temp);
}
else
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* ---------------- CHECKBOXES ---------------- */
/* verify parameters and draw button, returns 0 if OK else returns error message */
static short checkbox_open(short x,short y,BUTTON *button,fg_pbox_t area,DIALOG_ITEM *d)
{
if (button->name == NULL || (button->status & ~DIALOG_BITS) ||
button->accelerator < KEY_MIN || button->accelerator > KEY_MAX)
return M_INVALID_PARMS;
if (is_gray(*button))
{
checkbox_draw_gray(x,y,button,area,d->focus);
d->dialog_state |= DIALOG_FOCUS_SKIP;
}
else if (is_active(*button))
{
checkbox_draw(x,y,button,area,d->focus);
if (is_selected(*button))
checkbox_mark(button);
button->status |= DIALOG_OPENED;
}
else
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* draw a checkbox and fill the hotspot info */
static void checkbox_draw(short x,short y,BUTTON *b,fg_pbox_t clip,fg_pbox_t focus)
{
fg_box_t temp;
temp[FG_X1] = x + (gui_char_width*b->loc_x)/DIALOG_UNITS;
temp[FG_Y1] = y + (gui_char_height*b->loc_y)/DIALOG_UNITS;
temp[FG_X2] = dialog_draw_text(temp[FG_X1],temp[FG_Y1],b->name,clip);
temp[FG_X1] -= 2*gui_char_width;
temp[FG_Y2] = temp[FG_Y1] + gui_char_height;
fg_box_cpy(b->screen,temp);
temp[FG_X2] = temp[FG_X1] + gui_char_width;
temp[FG_Y1]++;
temp[FG_Y2]--;
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,clip);
fg_box_cpy(focus,temp);
}
/* draw a gray checkbox */
static void checkbox_draw_gray(short x,short y,BUTTON *b,fg_pbox_t clip,fg_pbox_t focus)
{
fg_box_t temp;
temp[FG_X1] = x + (gui_char_width*b->loc_x)/DIALOG_UNITS;
temp[FG_Y1] = y + (gui_char_height*b->loc_y)/DIALOG_UNITS;
temp[FG_X2] = dialog_draw_gray_text(temp[FG_X1],temp[FG_Y1],b->name,clip);
temp[FG_X1] -= 2*gui_char_width;
temp[FG_Y2] = temp[FG_Y1] + gui_char_height;
fg_box_cpy(b->screen,temp);
temp[FG_X2] = temp[FG_X1] + gui_char_width;
temp[FG_Y1]++;
temp[FG_Y2]--;
fg_drawbox(COLOR_DIALOG_GRAY,FG_MODE_SET,~0,FG_LINE_SOLID,temp,clip);
fg_box_cpy(focus,temp);
}
/* mark a check box by drawing an X and setting it selected */
static void checkbox_mark(BUTTON *b)
{
fg_line_t line;
fg_box_t clip;
fg_msm_hidecursor();
clip[FG_X1] = b->screen[FG_X1]+1;
clip[FG_Y1] = b->screen[FG_Y1]+2;
clip[FG_X2] = b->screen[FG_X1]+gui_char_width-1;
clip[FG_Y2] = b->screen[FG_Y2]-2;
line[FG_X1] = clip[FG_X1];
line[FG_Y1] = clip[FG_Y1];
line[FG_X2] = clip[FG_X2];
line[FG_Y2] = clip[FG_Y2];
fg_drawlineclip(COLOR_DIALOG_SELECTION,FG_MODE_SET,~0,FG_LINE_SOLID,line,clip);
line[FG_X1] = clip[FG_X1];
line[FG_Y1] = clip[FG_Y2];
line[FG_X2] = clip[FG_X2];
line[FG_Y2] = clip[FG_Y1];
fg_drawlineclip(COLOR_DIALOG_SELECTION,FG_MODE_SET,~0,FG_LINE_SOLID,line,clip);
b->status |= DIALOG_SELECTED;
fg_msm_showcursor();
fg_flush();
}
/* unmark a check box by clearing the X and setting it not selected */
static void checkbox_unmark(BUTTON *b)
{
fg_line_t line;
fg_box_t clip;
fg_msm_hidecursor();
clip[FG_X1] = b->screen[FG_X1]+1;
clip[FG_Y1] = b->screen[FG_Y1]+2;
clip[FG_X2] = b->screen[FG_X1]+gui_char_width-1;
clip[FG_Y2] = b->screen[FG_Y2]-2;
line[FG_X1] = clip[FG_X1];
line[FG_Y1] = clip[FG_Y1];
line[FG_X2] = clip[FG_X2];
line[FG_Y2] = clip[FG_Y2];
fg_drawlineclip(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,line,clip);
line[FG_X1] = clip[FG_X1];
line[FG_Y1] = clip[FG_Y2];
line[FG_X2] = clip[FG_X2];
line[FG_Y2] = clip[FG_Y1];
fg_drawlineclip(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,line,clip);
b->status &= ~DIALOG_SELECTED;
fg_msm_showcursor();
fg_flush();
}
/* ---------------- RADIOBUTTONS ---------------- */
/* verify parameters and draw buttons, returns 0 if OK else returns error message */
static short radiobutton_open(short x,short y,RADIOBUTTON *radio,fg_pbox_t area,DIALOG_ITEM *d)
{
short i;
BUTTON *b;
fg_box_t box;
if (radio->number_of_buttons < 0 || radio->number_of_buttons > DIALOG_MAX_ITEMS || (radio->status & ~DIALOG_BITS))
return M_INVALID_PARMS;
for (i = 0 ; i < radio->number_of_buttons ; i++)
{
b = &(radio->buttons[i]);
if (b->name == NULL || (b->status & ~DIALOG_BITS) ||
b->accelerator < KEY_MIN || b->accelerator > KEY_MAX)
return M_INVALID_PARMS;
}
if (is_active(*radio))
{
d->focus[FG_X1] = box[FG_X1] = -999;
for (i = 0 ; i < radio->number_of_buttons ; i++)
{
b = &(radio->buttons[i]);
if (is_gray(*b))
checkbox_draw_gray(x,y,b,area,box);
if (is_active(*b))
{
checkbox_draw(x,y,b,area,box);
if (is_selected(*b))
{
radiobutton_mark(b,NULL);
fg_box_cpy(d->focus,box);
}
}
}
if (d->focus[FG_X1] == -999) /* nothing selected, focus on last or skip */
if (box[FG_X1] == -999)
d->dialog_state |= DIALOG_FOCUS_SKIP;
else
fg_box_cpy(d->focus,box);
radio->status |= DIALOG_OPENED;
radio->dialog = d;
}
else
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* mark a radio button as on and set it as selected, also set focus */
static void radiobutton_mark(BUTTON *b,DIALOG_ITEM *d)
{
fg_box_t box;
fg_msm_hidecursor();
box[FG_X1] = b->screen[FG_X1];
box[FG_Y1] = b->screen[FG_Y1] + 1;
box[FG_X2] = box[FG_X1] + gui_char_width;
box[FG_Y2] = b->screen[FG_Y2] - 1;
if (d != NULL)
{
if (d->dialog_state & DIALOG_FOCUS_HERE)
{
dialog_focus_draw(d,FOCUS_CLEAR);
fg_box_cpy(d->focus,box);
dialog_focus_draw(d,FOCUS_SET);
}
else
fg_box_cpy(d->focus,box);
}
box[FG_X1] += 2;
box[FG_Y1] += 2;
box[FG_X2] -= 2;
box[FG_Y2] -= 2;
fg_boxclip(fg.displaybox,box,box);
fg_fillbox(COLOR_DIALOG_SELECTION,FG_MODE_SET,~0,box);
b->status |= DIALOG_SELECTED;
fg_msm_showcursor();
fg_flush();
}
/* unmark a radio button as off and set it as not selected */
static void radiobutton_unmark(BUTTON *b)
{
fg_box_t box;
fg_msm_hidecursor();
box[FG_X1] = b->screen[FG_X1]+2;
box[FG_Y1] = b->screen[FG_Y1]+3;
box[FG_X2] = b->screen[FG_X1]+gui_char_width-2;
box[FG_Y2] = b->screen[FG_Y2]-3;
fg_boxclip(fg.displaybox,box,box);
fg_fillbox(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,box);
b->status &= ~DIALOG_SELECTED;
fg_msm_showcursor();
fg_flush();
}
/* unmark any radio button which may be on and deselect it
* enter with pointer to first button
* returns a pointer to the unmarked button or NULL if none */
static BUTTON *radiobutton_unmark_all(BUTTON *first,short number_of_buttons)
{
BUTTON *b,*b2;
short i;
for (i = 0, b = first, b2 = NULL ; i < number_of_buttons ; i++, b++)
if (is_selected(*b))
{
radiobutton_unmark(b);
b2 = b;
}
return b2;
}
/* return the previous button in a radiobutton sequence */
static BUTTON *radiobutton_previous(BUTTON *first,short number_of_buttons)
{
short current,mark;
for (current = 0 ; current < number_of_buttons ; current++)
if (is_selected(*(first+current))) /* find current */
break;
mark = current;
do /* find previous */
{
if (--current < 0)
current = number_of_buttons-1;
if (current == mark)
break;
} while (is_inactive(*(first+current)));
return first+current;
}
/* return the next button in a radiobutton sequence */
static BUTTON *radiobutton_next(BUTTON *first,short number_of_buttons)
{
short current,mark;
for (current = 0 ; current < number_of_buttons ; current++)
if (is_selected(*(first+current))) /* find current */
break;
mark = current;
do /* find next */
{
if (++current >= number_of_buttons)
current = 0;
if (current == mark)
break;
} while (is_inactive(*(first+current)));
return first+current;
}
/* ---------------- LISTBOXES ---------------- */
/* verify parameters and open a listbox */
static short listbox_open(short x,short y,LISTBOX *list,fg_pbox_t area,DIALOG_ITEM *d)
{
fg_box_t temp;
if (list->width < DIALOG_UNITS || list->height < DIALOG_UNITS ||
list->first_item == NULL || list->next_item == NULL ||
(list->status & ~DIALOG_BITS))
return M_INVALID_PARMS;
if (is_active(*list))
{
list->status &= ~DIALOG_COMPLETED;
temp[FG_X1] = x+(gui_char_width*list->loc_x)/DIALOG_UNITS;
temp[FG_Y1] = y+(gui_char_height*list->loc_y)/DIALOG_UNITS;
temp[FG_X2] = temp[FG_X1]+gui_char_width*(list->width/DIALOG_UNITS+2)+5;
temp[FG_Y2] = temp[FG_Y1]+gui_char_height*(list->height/DIALOG_UNITS)+1;
fg_box_cpy(list->screen,temp);
fg_box_cpy(d->focus,temp);
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
temp[FG_X2] = temp[FG_X1] + gui_char_width + 1;
list->marker[FG_X1] = temp[FG_X1] + 1;
list->marker[FG_Y1] = temp[FG_Y1] + 1;
list->marker[FG_X2] = temp[FG_X2] - 1;
list->marker[FG_Y2] = temp[FG_Y2] - 1;
fg_boxclip(fg.displaybox,list->marker,list->marker);
list->listbox[FG_X1] = temp[FG_X2] + 2;
list->listbox[FG_Y1] = temp[FG_Y1] + 1;
list->listbox[FG_Y2] = temp[FG_Y2] - 1;
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
temp[FG_X2] = list->screen[FG_X2];
temp[FG_X1] = temp[FG_X2] - gui_char_width - 1;
list->listbox[FG_X2] = temp[FG_X1] - 2;
fg_boxclip(fg.displaybox,list->listbox,list->listbox);
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
temp[FG_Y2] = temp[FG_Y1] + (temp[FG_Y2] - temp[FG_Y1])/2;
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
fg_putc(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,temp[FG_X1]+1,list->screen[FG_Y2]-gui_char_height-1,LIST_UPARROW,area);
fg_putc(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,temp[FG_X1]+1,list->screen[FG_Y1]+1,LIST_DOWNARROW,area);
list->up[FG_X1] = list->down[FG_X1] = temp[FG_X1];
list->up[FG_X2] = list->down[FG_X2] = temp[FG_X2];
list->up[FG_Y1] = temp[FG_Y2] + 1;
list->up[FG_Y2] = list->screen[FG_Y2];
list->down[FG_Y1] = list->screen[FG_Y1];
list->down[FG_Y2] = temp[FG_Y2];
list->first_page = NULL;
list->status |= DIALOG_OPENED;
if (!listbox_initialize(list))
return M_NULL_ERROR;
}
else
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* free allocated listbox pages and mark as unopened */
static void listbox_close(LISTBOX *list)
{
if (list->status & DIALOG_OPENED)
{
listbox_free(list);
list->status &= ~(DIALOG_OPENED | DIALOG_COMPLETED);
}
}
/* free the pages allocated to the listbox */
static void listbox_free(LISTBOX *list)
{
LISTBOX_PAGE *lp,*lp2;
lp = list->first_page;
while (lp != NULL)
{
lp2 = lp->next_page;
free(lp);
lp = lp2;
}
list->first_page = NULL;
}
/* erase the current list box and redraw using list->current_page and list->page_offset as the top of the list */
static void listbox_draw_contents(LISTBOX *list)
{
short x,y,box_height,string_size,page_offset;
char *p;
LISTBOX_PAGE *lp;
fg_msm_hidecursor();
fg_fillbox(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,list->listbox); /* erase old list */
string_size = list->width/DIALOG_UNITS + 1; /* initialize variables */
box_height = list->height/DIALOG_UNITS;
lp = list->current_page;
x = list->listbox[FG_X1];
y = list->listbox[FG_Y2] - gui_char_height;
page_offset = list->page_offset;
p = lp->listbox_items + string_size * page_offset;
while (box_height)
{ /* for each line in the box */
if (page_offset >= lp->number_of_items)
{ /* if end of page, go to next page */
if (lp->next_page == NULL)
{ /* next page not found, try to load it */
if (!listbox_allocate_page(list,LISTBOX_SUCCEEDING_PAGES))
{ /* fill next page */
fg_msm_showcursor();
fg_flush();
return;
}
if (list->status & DIALOG_COMPLETED)
break; /* no more pages */
}
lp = lp->next_page; /* adjust page pointers to next page */
p = lp->listbox_items;
page_offset = 0;
}
fg_puts(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,x,y,p,list->listbox);
box_height--; /* adjust variables */
page_offset++;
y -= gui_char_height;
p += string_size;
}
fg_msm_showcursor();
fg_flush();
}
/* allocate a new listbox page and fill it, returns pointer to page if OK or NULL if failed */
static short listbox_allocate_page(LISTBOX *list,short type)
{
short i,string_size;
char *p;
LISTBOX_PAGE *lp,*lp2;
MESSAGE error;
if (type == LISTBOX_SUCCEEDING_PAGES && (list->status & DIALOG_COMPLETED))
return 1; /* list finished, no allocation needed */
string_size = list->width/DIALOG_UNITS + 1;
if ((lp = (LISTBOX_PAGE *) malloc(sizeof(LISTBOX_PAGE)+LISTBOX_MEM_PAGE_SIZE*string_size)) == NULL)
{ /* allocate page */
error.id = gui_errno = M_NOMEM;
error.data.ptr_data = listbox_allocate_page;
message_send(&error);
return 0;
}
lp->listbox_items = (char *) lp + sizeof(LISTBOX_PAGE); /* initialize page */
lp->number_of_items = 0;
if (type == LISTBOX_FIRST_PAGE)
{ /* first page is special */
lp->previous_page = NULL;
lp->next_page = NULL;
if ((*(list->first_item))(lp->listbox_items)) /* fill first page */
{
for (i=lp->number_of_items=1, p=lp->listbox_items+string_size ; i < LISTBOX_MEM_PAGE_SIZE ; i++, p+=string_size)
{ /* continue filling */
if ((*(list->next_item))(p))
lp->number_of_items++;
else
{
list->status |= DIALOG_COMPLETED; /* list complete in this page */
break;
}
}
}
else
list->status |= DIALOG_COMPLETED; /* empty list */
list->first_page = list->current_page = lp; /* preset list pointers and counters */
list->page_offset = list->screen_offset = 0;
}
else if (type == LISTBOX_SUCCEEDING_PAGES)
{
for (lp2 = list->current_page ; lp2->next_page != NULL ; lp2 = lp2->next_page)
; /* find end of list */
lp2->next_page = lp; /* thread page on to list */
lp->next_page = NULL;
lp->previous_page = lp2;
for (i=0, p=lp->listbox_items ; i < LISTBOX_MEM_PAGE_SIZE ; i++, p+=string_size)
{ /* fill page */
if ((*(list->next_item))(p))
lp->number_of_items++;
else
{
list->status |= DIALOG_COMPLETED; /* list complete in this page */
if (lp->number_of_items == 0)
{ /* nothing to put in this page */
lp2->next_page = NULL;
free(lp);
}
break;
}
}
}
else /* illegal type specifier */
{
free(lp);
error.id = gui_errno = M_INVALID_PARMS;
error.data.ptr_data = listbox_allocate_page;
message_send(&error);
return 0;
}
return 1;
}
/* mark active selection in list box */
static void listbox_mark(LISTBOX *list)
{
fg_msm_hidecursor();
fg_fillbox(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,list->marker); /* erase old marker */
if (list->first_page->next_page != NULL || list->first_page->number_of_items != 0)
fg_putc(COLOR_DIALOG_SELECTION,FG_MODE_SET,~0,FG_ROT0,list->marker[FG_X1],list->marker[FG_Y2]-gui_char_height*(list->screen_offset+1),LIST_RIGHTARROW,list->marker);
fg_msm_showcursor();
fg_flush();
}
/* scroll the listbox by the number of lines, returns 1 if OK or 0 if failed */
static short listbox_scroll(LISTBOX *list,short number_of_lines)
{
short height,ret;
LISTBOX_PAGE *lp;
short page_offset,height_count;
ret = 1;
height = list->height/DIALOG_UNITS;
if (number_of_lines < 0) /* scroll up */
{
if (list->screen_offset + number_of_lines >= 0)
list->screen_offset += number_of_lines; /* just move marker */
else
{ /* redraw page */
while (number_of_lines)
{
if (list->page_offset == 0)
{ /* get previous page */
if (list->current_page->previous_page == NULL)
{ /* no more previous pages */
list->screen_offset += number_of_lines;
if (list->screen_offset < 0)
list->screen_offset = 0;
break;
}
else
{ /* point to previous page */
list->current_page = list->current_page->previous_page;
list->page_offset = list->current_page->number_of_items;
}
}
list->page_offset--;
number_of_lines++;
}
listbox_draw_contents(list);
}
listbox_mark(list);
}
else if (number_of_lines > 0) /* scroll down */
{
for (lp=list->current_page, page_offset=list->page_offset, height_count=0 ; height_count < height-1 ; height_count++)
if (++page_offset >= lp->number_of_items) /* get pointer at bottom of screen */
{
if ((lp = lp->next_page) == NULL) break;
page_offset = 0;
}
if (lp == NULL || list->screen_offset+number_of_lines < height)
{ /* move within current screen or partially filled screen */
list->screen_offset += number_of_lines;
if (list->screen_offset > height_count)
list->screen_offset = height_count;
}
else
{ /* redraw page */
while (number_of_lines)
{
if (++page_offset >= lp->number_of_items)
{ /* need more pages */
if (lp->next_page == NULL)
{
ret = listbox_allocate_page(list,LISTBOX_SUCCEEDING_PAGES);
if (ret == 0 || lp->next_page == NULL)
{ /* nothing actually added */
list->status |= DIALOG_COMPLETED;
break;
}
}
lp = lp->next_page;
page_offset = 0;
}
list->page_offset++;
if (list->page_offset >= list->current_page->number_of_items)
{ /* get next page */
if (list->current_page->next_page == NULL)
{ /* this code should not be reached but is included as a safety plug */
list->page_offset--;
break;
}
list->current_page = list->current_page->next_page;
list->page_offset = 0;
}
number_of_lines--;
}
if (list->screen_offset + number_of_lines >= height)
{ /* still want more? move to bottom of page */
list->screen_offset = height-1;
}
listbox_draw_contents(list);
}
listbox_mark(list);
}
return ret;
}
/* return current data for listbox at offset from top, or NULL if none */
static char *listbox_current_data(LISTBOX *list,short offset)
{
LISTBOX_PAGE *lp;
short page_offset;
for (lp=list->current_page, page_offset=list->page_offset ; offset > 0 ; offset--)
{
page_offset++;
if (page_offset >= lp->number_of_items)
{ /* get next page */
if (lp->next_page == NULL)
return NULL;
lp = lp->next_page;
page_offset = 0;
}
}
return lp->listbox_items + page_offset * (list->width/DIALOG_UNITS + 1);
}
/* ---------------- EDITBOXES ---------------- */
/* verify parameters and open an editbox */
static short editbox_open(short x,short y,EDITBOX *edit,fg_pbox_t area,DIALOG_ITEM *d)
{
fg_box_t temp;
if (edit->edit_width < 1 || edit->edit_width < edit->screen_width/DIALOG_UNITS ||
edit->edit_string == NULL || (edit->status & ~DIALOG_BITS))
return M_INVALID_PARMS;
if (is_active(*edit))
{
temp[FG_X1] = x+(gui_char_width*edit->loc_x)/DIALOG_UNITS;
temp[FG_Y1] = y+(gui_char_height*edit->loc_y)/DIALOG_UNITS;
temp[FG_X2] = temp[FG_X1]+gui_char_width*(edit->screen_width/DIALOG_UNITS)+4;
temp[FG_Y2] = temp[FG_Y1]+gui_char_height+2;
fg_boxclip(fg.displaybox,temp,temp);
fg_box_cpy(edit->screen,temp);
fg_box_cpy(d->focus,temp);
fg_drawbox(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,temp,area);
edit->status |= DIALOG_OPENED;
editbox_initialize(edit);
}
else
d->dialog_state |= DIALOG_FOCUS_SKIP;
return 0;
}
/* strip trailing whitespace from string and mark as unopened */
static void editbox_close(EDITBOX *edit)
{
short i;
if (edit->status & DIALOG_OPENED)
{
edit->status &= ~DIALOG_OPENED;
for (i = edit->edit_width-1 ; i >= 0 ; i--)
if (edit->edit_string[i] == ' ')
edit->edit_string[i] = 0;
else
break;
}
}
/* draw the editbox contents starting from scroll_offset and place cursor */
static void editbox_draw(EDITBOX *edit)
{
fg_box_t temp;
short c,screen_width;
char *p;
p = edit->edit_string + edit->scroll_offset;
screen_width = edit->screen_width/DIALOG_UNITS;
fg_msm_hidecursor();
fg_box_cpy(temp,edit->screen);
temp[FG_X1]++;
temp[FG_Y1]++;
temp[FG_X2]--;
temp[FG_Y2]--;
fg_fillbox(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,temp);
c = p[screen_width];
p[screen_width] = 0;
fg_puts(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,temp[FG_X1],temp[FG_Y1],p,edit->screen);
p[screen_width] = c;
editbox_cursor(edit);
fg_msm_showcursor();
fg_flush();
}
/* move the editbox cursor */
static void editbox_cursor(EDITBOX *edit)
{
short x,y;
x = edit->screen[FG_X1] + 1;
y = edit->screen[FG_Y1] + 1;
fg_msm_hidecursor();
fg_putc(COLOR_DIALOG_BACKGROUND,FG_MODE_SET,~0,FG_ROT0,x+(edit->curpos-edit->scroll_offset)*gui_char_width,y,(edit->curtype)?(EDITBOX_CURSOR_INSERT):(EDITBOX_CURSOR_OVERWRITE),edit->screen);
fg_putc(COLOR_DIALOG_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,x+(edit->curpos-edit->scroll_offset)*gui_char_width,y,edit->edit_string[edit->curpos],edit->screen);
fg_putc(COLOR_DIALOG_SELECTION,FG_MODE_SET,~0,FG_ROT0,x+(edit->xpos-edit->scroll_offset)*gui_char_width,y,(edit->insert)?(EDITBOX_CURSOR_INSERT):(EDITBOX_CURSOR_OVERWRITE),edit->screen);
edit->curpos = edit->xpos;
edit->curtype = edit->insert;
fg_msm_showcursor();
fg_flush();
}
/* using key, edit the line in the editbox.
* If any edit changes were made return 2, if the key was consumed return 1,
* else return 0 */
static short editbox_edit(EDITBOX *edit,short key)
{
short ret,width,i,x,scrolled,len;
char *str;
ret = scrolled = 0; /* preset variables */
x = edit->xpos;
width = edit->edit_width;
str = edit->edit_string;
switch (key)
{
case LEFTARROW: /* move left */
if (x > 0)
x--;
ret = 1;
break;
case RIGHTARROW: /* move right */
if (str[x] == 0)
{
str[x+1] = 0;
str[x] = ' ';
}
if (x < width-1)
x++;
ret = 1;
break;
case EDITBOX_HOME: /* move to beginning */
x = 0;
ret = 1;
break;
case EDITBOX_END: /* move to end */
for (x = strlen(str) ; x > 0 ; x--)
if (str[x-1] != ' ')
break;
if (x >= width)
x--;
ret = 1;
break;
case INS: /* toggle insert mode */
edit->insert ^= 1;
ret = 1;
break;
case DELETE: /* delete at cursor */
for (i = x ; i < strlen(str) ; i++)
str[i] = str[i+1];
ret = 2;
break;
case EDITBOX_DELETE_EOL: /* delete to end of line */
str[x] = 0;
ret = 2;
break;
case BKSP: /* delete to right */
if (x == 0)
break;
for (i = --x ; i < strlen(str) ; i++)
str[i] = str[i+1];
ret = 2;
break;
default: /* if printable, print it */
if (!isprint_ibm(key))
break;
if (edit->insert)
{
len = strlen(str);
if (len < width)
str[len+1] = 0;
else
len--;
for (i = len ; i > x ; i--)
str[i] = str[i-1];
}
else
if (str[x] == 0)
str[x+1] = 0;
str[x] = key;
if (x < width-1)
x++;
ret = 2;
break;
}
edit->xpos = x;
if (x < edit->scroll_offset)
{
edit->scroll_offset = x;
scrolled = 1;
}
if (x >= edit->scroll_offset+edit->screen_width/DIALOG_UNITS)
{
edit->scroll_offset = x - edit->screen_width/DIALOG_UNITS + 1;
scrolled = 1;
}
if (ret == 2 || scrolled == 1) /* rewrite editbox if changed or scrolled */
editbox_draw(edit);
if (ret == 1 && scrolled == 0) /* just move cursor if changed */
editbox_cursor(edit);
return ret;
}